package cn.lg.soar.system.auth.service.impl;

import cn.lg.soar.common.util.AssertUtil;
import cn.lg.soar.common.util.BeanCopier;
import cn.lg.soar.common.util.data.StringUtil;
import cn.lg.soar.core.annotations.SoarCache;
import cn.lg.soar.database.base.service.impl.BaseServiceImpl;
import cn.lg.soar.system.api.model.UserCreateDTO;
import cn.lg.soar.system.auth.entity.AccountBind;
import cn.lg.soar.system.auth.entity.User;
import cn.lg.soar.system.auth.mapper.AccountBindMapper;
import cn.lg.soar.system.auth.mapper.UserMapper;
import cn.lg.soar.system.auth.model.UserDetailVo;
import cn.lg.soar.system.auth.model.UserOrgItemVO;
import cn.lg.soar.system.auth.service.*;
import cn.lg.soar.system.config.AuthCacheKey;
import cn.lg.soar.system.monitor.service.IRefreshTokenService;
import cn.lg.soar.system.util.PasswordUtils;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author luguoxiang
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
 */
@RefreshScope
public class UserServiceImpl extends BaseServiceImpl<UserMapper, User> implements IUserService {

    @Autowired
    private IRoleService roleService;
    @Autowired
    private IOrgService orgService;
    @Autowired
    private UserRoleService userRoleService;
    @Autowired
    private UserOrgService userOrgService;
    @Autowired
    private OrgRoleService orgRoleService;
    @Autowired
    private PostRoleService postRoleService;
    @Autowired
    private ITemporaryPasswordService temporaryPasswordService;
    @Autowired
    private IRefreshTokenService refreshTokenService;
    @Autowired
    private IUserCurrBindService userCurrBindService;
    @Autowired
    private AccountBindMapper accountBindMapper;

    @Value("${lg.security.super-administrators:dev}")
    private Set<String> superAdministrators;

    @Value("${lg.auth.default-password:e10adc3949ba59abbe56e057f20f883e}")
    private String defaultPassword;

    private final static BeanCopier<UserCreateDTO, User> DTO_TO_ENTITY = BeanCopier.create(UserCreateDTO.class, User.class);

    @SoarCache(value = AuthCacheKey.USER_BY_ID, key = "#id")
//    @Cacheable(value = AuthCacheKey.USER_BY_ID, key = "#id")
    @Override
    public User getById(Serializable id) {
        return super.getById(id);
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public Long create(UserCreateDTO data) {
        User user = DTO_TO_ENTITY.convert(data);
        String password = user.getPassword();
        if (StringUtil.isBlank(password)) {
            password = defaultPassword;
        }
        user.setSecret(IdWorker.get32UUID());
        password = PasswordUtils.encrypt(password, user.getSecret(), user.getUsername());
        user.setPassword(password);
        AssertUtil.isTrue(this.save(user), "创建用户失败");
        // 配置用户角色
        if (!CollectionUtils.isEmpty(data.getRoleIds())) {
            userRoleService.setUserRole(user.getId(), data.getRoleIds());
        }
//        // 配置用户组织机构
//        if (!CollectionUtils.isEmpty(data.getOrgIds())) {
//            userOrgService.setUserOrg(user.getId(), data.getOrgIds());
//        }
//        // 配置用户岗位
//        if (!CollectionUtils.isEmpty(data.getPostIds())) {
//            userPostService.setUserPost(user.getId(), data.getPostIds());
//        }
        return user.getId();
    }

    @CacheEvict(value = AuthCacheKey.USER_BY_ID, key = "#entity.id")
    @Transactional(rollbackFor = RuntimeException.class)
    @Override
    public boolean updateById(User entity) {
        return super.updateById(entity);
    }

    @CacheEvict(value = AuthCacheKey.USER_BY_ID, key = "#id")
    @Override
    public boolean removeById(Serializable id) {
        User user = this.proxy().getById(id);
        AssertUtil.notNull(user, "用户不存在");
        AssertUtil.isFalse(this.superAdministrators.contains(user.getUsername()), "不能删除超级管理员");
        // 删除角色、机构和岗位关系
        userRoleService.clearByUser(user.getId());
        userOrgService.clearByUser(user.getId());
        // 强制下线 TODO 访问token依然有效
        this.refreshTokenService.removeByUserId(id);
        return super.removeById(id);
    }

    @Transactional(rollbackFor = RuntimeException.class)
    @Override
    public boolean removeByIds(Collection<?> ids) {
        IUserService self = this.proxy();
        for (Object id : ids) {
            AssertUtil.isTrue(self.removeById((Serializable) id), "删除{0}失败", id);
        }
        userCurrBindService.removeByIds(ids);
        return true;
    }

    @Override
    public void updateStatus(Integer status, Collection<Long> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            return;
        }
        List<User> list = list(
                Wrappers.<User>lambdaQuery()
                        .in(User::getId, ids)
                        .in(User::getUsername, this.superAdministrators)
        );
        AssertUtil.isTrue(list.isEmpty(), "需超级管理员权限");
        update(
                Wrappers.<User>lambdaUpdate()
                        .set(User::getStatus, status)
                        .in(User::getId, ids)
        );
        IUserService proxy = proxy();
        for (Long id : ids) {
            proxy.clearCache(id);
        }
    }

    @Transactional(rollbackFor = RuntimeException.class)
    @Override
    public void updatePassword(final String password, Collection<Long> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            return;
        }
        IUserService proxy = proxy();
        for (Long id : ids) {
            User user = proxy.getById(id);
            AssertUtil.notNull(user, "用户不存在");
            String secret = IdWorker.get32UUID();
            User updateUser = new User();
            updateUser.setId(id);
            updateUser.setSecret(secret);
            updateUser.setPassword(PasswordUtils.encrypt(password, secret, user.getUsername()));
            AssertUtil.isTrue(this.updateById(updateUser), "修改密码失败");
            proxy.clearCache(id);
        }
    }

    @Transactional(rollbackFor = RuntimeException.class)
    @Override
    public void resetPassword(Collection<Long> ids) {
        updatePassword(defaultPassword, ids);
    }

    @Override
    public UserDetailVo detail(long id) {
        User user = this.proxy().getById(id);
        if (user == null) {
            return null;
        }
        UserDetailVo convert = new UserDetailVo(user);
        // 查角色
        List<Long> roleIds = userRoleService.getRoleIds(id);
        convert.setRoles(roleService.listByIds(roleIds));
        // 查组织机构
        List<UserOrgItemVO> orgs = userOrgService.getOrgs(id);
        convert.setOrgs(orgs);
        // 查账号绑定
        List<AccountBind> accountBinds = accountBindMapper.selectList(
                Wrappers.<AccountBind>lambdaQuery()
                        .eq(AccountBind::getUserId, id)
        );
        convert.setAccountBinds(accountBinds);

        return convert;
    }

    @Override
    public boolean verifyPassword(User user, String password) {
        // 验证用户密码
        if (PasswordUtils.verify(password, user.getSecret(), user.getUsername(), user.getPassword())) {
            return true;
        }
        // 使用临时密码登录
        return temporaryPasswordService.verifyPassword(user.getId(), user.getUsername(), password);
    }

    @Override
    public User getByUsername(String username) {
        return super.getOne(
                Wrappers.<User>lambdaQuery()
                        .eq(User::getUsername, username)
        );
    }

    @Override
    public boolean isSuperAdministrator(String username) {
        return this.superAdministrators.contains(username);
    }

    @Override
    public boolean isSuperAdministrator(Long userId) {
        User user = proxy().getById(userId);
        if (user == null) {
            return false;
        }
        return isSuperAdministrator(user.getUsername());
    }

    @CacheEvict(value = {
            AuthCacheKey.USER_BY_ID,
            AuthCacheKey.ROLE_ID_BY_USER,
            AuthCacheKey.ORG_ID_BY_USER,
            AuthCacheKey.ORG_ID_BY_USER_ALL,
            AuthCacheKey.ROLE_ID_BY_USER_ALL
    }, key = "#userId")
    @Override
    public void clearCache(Long userId) {}

    @Cacheable(value = AuthCacheKey.ROLE_ID_BY_USER_ALL, key = "#userId", cacheManager = "local")
    @Override
    public List<Long> getUserCurrentAllRoleIds(Long userId) {
        // 查用户关联的角色
        List<Long> roleIds = userRoleService.getRoleIds(userId);

        // 获取用户机构关联的角色
        List<Long> orgIds = userOrgService.getOrgIds(userId);
        if (CollectionUtils.isNotEmpty(orgIds)) {
            for (Long orgId : orgIds) {
                roleIds.addAll(orgRoleService.getRoleIds(orgId));
            }
        }

        // 获取用户岗位关联的角色
        List<Long> postIds = userOrgService.getPostIds(userId);
        if (CollectionUtils.isNotEmpty(postIds)) {
            for (Long postId : postIds) {
                roleIds.addAll(postRoleService.getRoleIds(postId));
            }
        }

        // 去重后查角色相关的角色
        roleIds = roleIds.stream().distinct().collect(Collectors.toList());
        List<Long> result = new ArrayList<>(roleIds.size() << 2);
        result.addAll(roleIds);
        for (Long roleId : roleIds) {
            result.addAll(roleService.getRelevantIds(roleId));
        }

        // 再次去重得到用户所有角色
        return result.stream().distinct().collect(Collectors.toList());
    }

    @Cacheable(value = AuthCacheKey.ORG_ID_BY_USER_ALL, key = "#userId", cacheManager = "local")
    @Override
    public List<Long> getUserCurrentAllOrgIds(Long userId) {
        List<Long> orgIds = userOrgService.getOrgIds(userId);
        List<Long> inferiorsIds = orgService.getInferiorsIds(orgIds);
        inferiorsIds.addAll(orgIds);
        return inferiorsIds;
    }

}
